home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-04 / ms_sh21s.zip / SH210 / SRC / SH9.C < prev    next >
C/C++ Source or Header  |  1992-12-14  |  47KB  |  2,166 lines

  1. /* MS-DOS SHELL - History Processing
  2.  *
  3.  * MS-DOS SHELL - Copyright (c) 1990,1,2 Data Logic Limited
  4.  *
  5.  * This code is subject to the following copyright restrictions:
  6.  *
  7.  * 1.  Redistribution and use in source and binary forms are permitted
  8.  *     provided that the above copyright notice is duplicated in the
  9.  *     source form and the copyright notice in file sh6.c is displayed
  10.  *     on entry to the program.
  11.  *
  12.  * 2.  The sources (or parts thereof) or objects generated from the sources
  13.  *     (or parts of sources) cannot be sold under any circumstances.
  14.  *
  15.  *    $Header: /usr/users/istewart/src/shell/sh2.1/RCS/sh9.c,v 2.5 1992/12/14 10:54:56 istewart Exp $
  16.  *
  17.  *    $Log: sh9.c,v $
  18.  *    Revision 2.5  1992/12/14  10:54:56  istewart
  19.  *    BETA 215 Fixes and 2.1 Release
  20.  *
  21.  *    Revision 2.4  1992/11/06  10:03:44  istewart
  22.  *    214 Beta test updates
  23.  *
  24.  *    Revision 2.3  1992/09/03  18:54:45  istewart
  25.  *    Beta 213 Updates
  26.  *
  27.  *    Revision 2.2  1992/07/16  14:33:34  istewart
  28.  *    Beta 212 Baseline
  29.  *
  30.  *    Revision 2.1  1992/07/10  10:52:48  istewart
  31.  *    211 Beta updates
  32.  *
  33.  *    Revision 2.0  1992/05/07  21:33:35  Ian_Stewartson
  34.  *    MS-Shell 2.0 Baseline release
  35.  *
  36.  */
  37.  
  38. #include <sys/types.h>
  39. #include <stdio.h>
  40. #include <conio.h>
  41. #include <string.h>
  42. #include <memory.h>
  43. #include <ctype.h>
  44. #include <signal.h>
  45. #include <stdlib.h>
  46. #include <stddef.h>
  47. #include <errno.h>
  48. #include <setjmp.h>
  49. #include <limits.h>
  50. #ifdef OS2
  51. #define INCL_DOSSESMGR
  52. #define INCL_VIO
  53. #define INCL_KBD
  54. #include <os2.h>
  55. #else
  56. #include <dos.h>
  57. #endif
  58. #include <unistd.h>
  59. #include <dirent.h>
  60. #include "sh.h"
  61.  
  62. /* Keyboard functions */
  63.  
  64. #define KF_LENGTH    (sizeof (KF_List) / sizeof (KF_List[0]))
  65. #define KF_SCANBACKWARD    0x00        /* Scan backwards in history    */
  66. #define KF_SCANFOREWARD    0x01        /* Scan forewards in history    */
  67. #define KF_PREVIOUS    0x02        /* Previous command        */
  68. #define KF_NEXT        0x03        /* Next command            */
  69. #define KF_LEFT        0x04        /* Left one character        */
  70. #define KF_RIGHT    0x05        /* Right one character        */
  71. #define KF_WORDRIGHT    0x06        /* Right one word        */
  72. #define KF_WORDLEFT    0x07        /* Left one word        */
  73. #define KF_START    0x08        /* Move to start of line    */
  74. #define KF_CLEAR    0x09        /* Clear input line        */
  75. #define KF_FLUSH    0x0a        /* Flush to end of line        */
  76. #define KF_END        0x0b        /* End of line            */
  77. #define KF_INSERT    0x0c        /* Insert mode switch        */
  78. #define KF_DELETERIGHT    0x0d        /* Delete right character    */
  79. #define KF_DELETELEFT    0x0e        /* Delete left character    */
  80. #define KF_COMPLETE    0x0f        /* Complete file name        */
  81. #define KF_DIRECTORY    0x10        /* Complete directory function    */
  82. #define KF_CLEARSCREEN    0x11        /* Clear screen            */
  83. #define KF_JOBS        0x12        /* Print Job tree        */
  84. #define KF_TRANSPOSE    0x13        /* Transpose characters        */
  85. #define KF_QUOTE    0x14        /* Quote character        */
  86. #define KF_END_FKEYS    0x15        /* End of function keys        */
  87. #define KF_RINGBELL    0x15        /* Ring bell            */
  88. #define KF_HALFHEIGTH    0x16        /* Half height cursor        */
  89. #define KF_INSERTMODE    0x17        /* Overstrike or Insert        */
  90. #define KF_INSERTCURSOR    0x18        /* Enable insert cursor        */
  91. #define KF_ROOTDRIVE    0x19        /* Root drive            */
  92. #define KF_EOFKEY    0x1a        /* EOF value            */
  93.  
  94. /* Function Declarations */
  95.  
  96. #ifndef NO_HISTORY
  97. static bool near    RingWarningBell (void);
  98. static bool near    ProcessAlphaNumericKey (int);
  99. static bool near    ProcessFunctionKey (int);
  100. static bool near    Process_History (int);
  101. static bool near    ScanHistory (void);
  102. static void near    ReDisplayInputLine (void);
  103. static void near    PageHistoryRecord (int);
  104. static bool near    UpdateConsoleInputLine (char *);
  105. static bool near    ReStartInput (char *);
  106. static void near    SetCursorPosition (int);
  107. static void near    GenerateNewCursorPosition (void);
  108. static void near    EraseToEndOfLine (void);
  109. static void near    SetCursorShape (bool);
  110. static bool near    CompleteFileName (char *, bool);
  111. static void near    InitialiseInput (bool);
  112. static void near    PrintOutHistory (FILE *, bool, struct cmd_history *);
  113. static void near    ReleaseCommandMemory (struct cmd_history *);
  114. static void near    SaveCurrentHistory (void);
  115. static bool near    ClearScreen (void);
  116. static void near    memrcpy (char *, char *, int);
  117. static FILE * near    OpenHistoryFile (char *);
  118. #endif
  119.  
  120. #ifndef OS2
  121. static void near    CheckKeyboardPolling (void);
  122. #endif
  123.  
  124. static unsigned char near    ReadKeyBoard (unsigned char *);
  125. static int near            ReadCursorPosition (void);
  126. static void near        GetScreenParameters (void);
  127. static bool near        InsertACharacter (int);
  128. static int near            OutputACharacter (int);
  129.  
  130. static int    s_cursor;        /* Start cursor position    */
  131. static int    MaximumColumns = 80;    /* Max columns            */
  132. static int    MaximumLines = 25;    /* Max Lines            */
  133. #ifndef NO_HISTORY
  134. static bool    InsertMode = FALSE;
  135. static char    *c_buffer_pos;        /* Position in command line    */
  136. static char    *EndOfCurrentLine;    /* End of command line        */
  137. static int    m_line = 0;        /* Max write line number    */
  138. static int    c_history = -1;        /* Current entry        */
  139. static int    l_history = 0;        /* End of history array        */
  140. static int    M_length = -1;        /* Match length            */
  141. static int    Max_Length = 0;        /* Max line length        */
  142. static int    CurrentHistorySize = 0;    /* Current Length of History    */
  143.                     /* Array            */
  144. static bool    AppendHistory = FALSE;    /* Append to history        */
  145. static bool    SaveHistory = FALSE;    /* Save history            */
  146. static char    *No_prehistory   = "No previous commands";
  147. static char    *No_MatchHistory = "No history match found";
  148. static char    *No_posthistory  = "No more commands";
  149. static char    *History_2long   = "History line too long";
  150.  
  151. /* Function Key table */
  152.  
  153. static struct Key_Fun_List {
  154.     char        *kf_name;
  155.     unsigned char    akey;
  156.     unsigned char    fkey;
  157.     unsigned char    fcode;
  158. } KF_List[] = {
  159.     { "ScanBackward",    0,    'I',    KF_SCANBACKWARD },
  160.     { "ScanForeward",    0,    'Q',    KF_SCANFOREWARD },
  161.     { "Previous",    0,    'H',    KF_PREVIOUS },
  162.     { "Next",        0,    'P',    KF_NEXT },
  163.     { "Left",        0,    'K',    KF_LEFT },
  164.     { "Right",        0,    'M',    KF_RIGHT },
  165.     { "WordRight",    0,    't',    KF_WORDRIGHT },
  166.     { "WordLeft",    0,    's',    KF_WORDLEFT },
  167.     { "Start",        0,    'G',    KF_START },
  168.     { "Clear",        0,    'v',    KF_CLEAR },
  169.     { "Flush",        0,    'u',    KF_FLUSH },
  170.     { "End",        0,    'O',    KF_END },
  171.     { "Insert",        0,    'R',    KF_INSERT },
  172.     { "DeleteRight",    0,    'S',    KF_DELETERIGHT },
  173.     { "DeleteLeft",    0x08,    0,    KF_DELETELEFT },
  174.     { "Complete",    0,    'w',    KF_COMPLETE },
  175.     { "Directory",    0,    0x0f,    KF_DIRECTORY },
  176.     { "ClearScreen",    0,    0x84,    KF_CLEARSCREEN },
  177.     { "Jobs",        0,    0x68,    KF_JOBS },
  178.     { "Transpose",    0x14,    0,    KF_TRANSPOSE },
  179.     { "Quote",        0x11,    0,    KF_QUOTE },
  180.  
  181. /* End of function keys - flags */
  182.  
  183.     { "Bell",        1,    0,    KF_RINGBELL },
  184.     { "HalfHeight",    0,    0,    KF_HALFHEIGTH },
  185.     { "InsertMode",    0,    0,    KF_INSERTMODE },
  186.     { "InsertCursor",    1,    0,    KF_INSERTCURSOR },
  187.     { "RootDrive",    0,    0,    KF_ROOTDRIVE },
  188.     { "EOFKey",        4,    0,    KF_EOFKEY }
  189. };
  190.  
  191. /* Arrary of history Items */
  192.  
  193. struct    cmd_history {
  194.     int        number;
  195.     char    *command;
  196. };
  197.  
  198. static struct cmd_history    *cmd_history = (struct cmd_history *)NULL;
  199.  
  200. /* Processing standard input */
  201.  
  202. int GetConsoleInput (register IO_Args *ap)
  203. {
  204.     int        coff = (int)ap->afpos;
  205.     char    rv;
  206.  
  207. /* Is there anything in the input buffer.  If not, add the previous line to
  208.  * the history buffer and get the next line
  209.  */
  210.  
  211.     if (!coff)
  212.     {
  213.  
  214. /* Has dofc set the flag to say use the console buffer ? */
  215.  
  216.     if (UseConsoleBuffer)
  217.     {
  218.         UseConsoleBuffer = FALSE;
  219.         SaveHistory = TRUE;
  220.         SaveCurrentHistory ();
  221.         SaveHistory = FALSE;
  222.     }
  223.  
  224.     else
  225.         GetLineFromConsole ();        /* No - get input    */
  226.     }
  227.  
  228. /* Get the next character */
  229.  
  230.     if ((rv = ConsoleLineBuffer[coff]) == 0)
  231.     {
  232.     ConsoleLineBuffer[coff] = 0;
  233.     ap->afpos = 0L;
  234.     rv = CHAR_NEW_LINE;
  235.     }
  236.  
  237. /* Check for end of file */
  238.  
  239.     else if (rv == (char)KF_List[KF_EOFKEY].akey)
  240.     {
  241.     ConsoleLineBuffer[coff] = 0;
  242.     ap->afpos = 0L;
  243.     rv = 0;
  244.     }
  245.  
  246.     else
  247.     ap->afpos++;
  248.  
  249.     return rv;
  250. }
  251.  
  252. /*
  253.  * Read line from Console
  254.  */
  255.  
  256. void GetLineFromConsole (void)
  257. {
  258.     unsigned char    a_key, f_key;
  259.     int            i;
  260.  
  261. /* Set to last history item */
  262.  
  263.     SaveCurrentHistory ();
  264.  
  265. /* Zap the line and output the prompt */
  266.  
  267.     memset (ConsoleLineBuffer, 0, LINE_MAX + 1);
  268.     OutputUserPrompt (LastUserPrompt);
  269.  
  270. #ifndef OS2
  271.     CheckKeyboardPolling ();
  272. #endif
  273.  
  274.     if (ChangeInitLoad)
  275.     {
  276.     ChangeInitLoad = FALSE;
  277.         Configure_Keys ();
  278.     }
  279.  
  280. /* Process the input */
  281.  
  282.     while (TRUE)
  283.     {
  284.     InitialiseInput ((bool)KF_List[KF_INSERTMODE].akey);
  285.  
  286.     while (((a_key = ReadKeyBoard (&f_key)) != KF_List[KF_EOFKEY].akey) &&
  287.            (a_key != CHAR_NEW_LINE) && (a_key != CHAR_RETURN))
  288.     {
  289.  
  290. /* Look up the keystroke to see if it is one of our functions */
  291.  
  292.         for (i = 0; (i < KF_END_FKEYS); ++i)
  293.         {
  294.         if (KF_List[i].akey != a_key)
  295.             continue;
  296.  
  297. /* Function or meta key? */
  298.  
  299.         if ((a_key != 0) && (a_key != 0xff))
  300.             break;
  301.         
  302.         else if (KF_List[i].fkey == f_key)
  303.             break;
  304.         }
  305.  
  306. /* If this is a function key and is not ours, ignore it */
  307.  
  308.         if ((i == KF_END_FKEYS) && ((a_key == 0) || (a_key == 0x0ff)))
  309.         continue;
  310.  
  311.         if (((i == KF_END_FKEYS) ? ProcessAlphaNumericKey (a_key)
  312.                      : ProcessFunctionKey (KF_List[i].fcode)))
  313.         ReDisplayInputLine ();
  314.  
  315. /* Reposition the cursor */
  316.  
  317.         GenerateNewCursorPosition ();
  318.     }
  319.  
  320. /* Terminate the line */
  321.  
  322.     *EndOfCurrentLine = 0;
  323.     putchar (CHAR_NEW_LINE);
  324.     s_cursor = -1;
  325.     fflush (stdout);
  326.  
  327. /* Line input - check for history */
  328.  
  329.     if ((*ConsoleLineBuffer == '!') && Process_History (0))
  330.     {
  331.         puts (ConsoleLineBuffer);
  332.         break;
  333.     }
  334.  
  335.     else if (*ConsoleLineBuffer != '!')
  336.         break;
  337.     }
  338.  
  339.     if (LastUserPrompt == PS1)
  340.     LastUserPrompt = PS2;
  341.  
  342.     fflush (stdout);
  343.     SetCursorShape (FALSE);
  344.  
  345.     if (a_key == KF_List[KF_EOFKEY].akey)
  346.     *EndOfCurrentLine = KF_List[KF_EOFKEY].akey;
  347.  
  348.     return;
  349. }
  350.  
  351. /* Handler Alpha_numeric characters */
  352.  
  353. static bool near ProcessAlphaNumericKey (int c)
  354. {
  355.     bool    redisplay = FALSE;
  356.  
  357. /* Normal character processing */
  358.  
  359.     if ((c_buffer_pos - ConsoleLineBuffer) == LINE_MAX)
  360.     return RingWarningBell ();
  361.  
  362.     else if (!InsertMode)
  363.     {
  364.     if (c_buffer_pos == EndOfCurrentLine)
  365.         ++EndOfCurrentLine;
  366.  
  367.     else if (iscntrl (*c_buffer_pos) || iscntrl (c))
  368.         redisplay = TRUE;
  369.  
  370.     *(c_buffer_pos++) = (char)c;
  371.  
  372.     if (redisplay || (c == CHAR_TAB))
  373.         return TRUE;
  374.  
  375. /* Output the character */
  376.  
  377.     OutputACharacter (c);
  378.     return FALSE;
  379.     }
  380.  
  381.     else
  382.     return InsertACharacter (c);
  383. }
  384.  
  385. /* Process function keys */
  386.  
  387. static bool near ProcessFunctionKey (int fn)
  388. {
  389.     bool    fn_search = FALSE;
  390.     char    tmp;
  391.  
  392.     switch (fn)
  393.     {
  394.     case KF_SCANBACKWARD:        /* Scan backwards in history    */
  395.     case KF_SCANFOREWARD:        /* Scan forewards in history    */
  396.         *EndOfCurrentLine = 0;
  397.  
  398.         if (M_length == -1)
  399.         M_length = strlen (ConsoleLineBuffer);
  400.  
  401.         PageHistoryRecord ((fn == KF_SCANBACKWARD) ? -1 : 1);
  402.         return TRUE;
  403.  
  404.     case KF_PREVIOUS:        /* Previous command        */
  405.         *EndOfCurrentLine = 0;
  406.         Process_History (-1);
  407.         return TRUE;
  408.  
  409.     case KF_NEXT:            /* Next command line        */
  410.         Process_History (1);
  411.         return TRUE;
  412.  
  413.     case KF_LEFT:            /* Cursor left            */
  414.         if (c_buffer_pos != ConsoleLineBuffer)
  415.         --c_buffer_pos;
  416.  
  417.         else
  418.         RingWarningBell ();
  419.  
  420.         return FALSE;
  421.  
  422.     case KF_RIGHT:            /* Cursor right            */
  423.         if (c_buffer_pos != EndOfCurrentLine)
  424.         ++c_buffer_pos;
  425.  
  426.         else
  427.         RingWarningBell ();
  428.  
  429.         return FALSE;
  430.  
  431.     case KF_WORDLEFT:        /* Cursor left a word        */
  432.         if (c_buffer_pos != ConsoleLineBuffer)
  433.         {
  434.         --c_buffer_pos;        /* Reposition on previous char    */
  435.  
  436.         while (isspace (*c_buffer_pos) &&
  437.                (c_buffer_pos != ConsoleLineBuffer))
  438.             --c_buffer_pos;
  439.  
  440.         while (!isspace (*c_buffer_pos) &&
  441.                (c_buffer_pos != ConsoleLineBuffer))
  442.             --c_buffer_pos;
  443.  
  444.         if (c_buffer_pos != ConsoleLineBuffer)
  445.             ++c_buffer_pos;
  446.         }
  447.  
  448.         else
  449.         RingWarningBell ();
  450.  
  451.         return FALSE;
  452.  
  453.     case KF_WORDRIGHT:        /* Cursor right a word        */
  454.         if (c_buffer_pos != EndOfCurrentLine)
  455.         {
  456.  
  457. /* Skip to the end of the current word */
  458.  
  459.         while (!isspace (*c_buffer_pos) &&
  460.                (c_buffer_pos != EndOfCurrentLine))
  461.             ++c_buffer_pos;
  462.  
  463. /* Skip over the white space */
  464.  
  465.         while (isspace (*c_buffer_pos) &&
  466.                (c_buffer_pos != EndOfCurrentLine))
  467.             ++c_buffer_pos;
  468.         }
  469.  
  470.         else
  471.         RingWarningBell ();
  472.  
  473.         return FALSE;
  474.  
  475.     case KF_START:            /* Cursor home            */
  476.         c_buffer_pos = ConsoleLineBuffer;
  477.         return FALSE;
  478.  
  479.     case KF_CLEAR:            /* Erase buffer            */
  480.         c_buffer_pos = ConsoleLineBuffer;
  481.  
  482.     case KF_FLUSH:            /* Flush to end            */
  483.         memset (c_buffer_pos, CHAR_SPACE, EndOfCurrentLine - c_buffer_pos);
  484.         EndOfCurrentLine = c_buffer_pos;
  485.         return TRUE;
  486.  
  487.     case KF_END:            /* Cursor end of command    */
  488.         if (*ConsoleLineBuffer == '!')
  489.         {
  490.         *EndOfCurrentLine = 0;
  491.         Process_History (2);
  492.         return TRUE;
  493.         }
  494.  
  495.         c_buffer_pos = EndOfCurrentLine;
  496.         return FALSE;
  497.  
  498.     case KF_INSERT:            /* Switch insert mode        */
  499.         InsertMode = (InsertMode) ? FALSE : TRUE;
  500.         SetCursorShape (InsertMode);
  501.         return FALSE;
  502.  
  503.     case KF_CLEARSCREEN:        /* Clear the screen        */
  504.         return ClearScreen ();
  505.  
  506.     case KF_TRANSPOSE:        /* Transpose characters        */
  507.         if(c_buffer_pos == ConsoleLineBuffer)
  508.         return RingWarningBell ();
  509.         
  510.         if (c_buffer_pos == EndOfCurrentLine)
  511.         --c_buffer_pos;
  512.  
  513.         tmp = *(c_buffer_pos - 1);
  514.         *(c_buffer_pos - 1) = *c_buffer_pos;
  515.         *c_buffer_pos = tmp;
  516.  
  517.         if (c_buffer_pos != EndOfCurrentLine)
  518.         ++c_buffer_pos;
  519.  
  520.         return TRUE;
  521.  
  522.     case KF_QUOTE:            /* Quote characters        */
  523.         return ProcessAlphaNumericKey (ReadKeyBoard (&tmp));
  524.  
  525. #ifdef OS2
  526.     case KF_JOBS:            /* Print Job Tree        */
  527.         putchar (CHAR_NEW_LINE);
  528.         PrintProcessTree (getpid ());
  529.         OutputUserPrompt (LastUserPrompt);
  530.         s_cursor = ReadCursorPosition ();
  531.         return TRUE;
  532. #endif
  533.  
  534.     case KF_DELETERIGHT:        /* Delete right character    */
  535.         if (c_buffer_pos == EndOfCurrentLine)
  536.         return FALSE;
  537.  
  538.         memcpy (c_buffer_pos, c_buffer_pos + 1,
  539.             EndOfCurrentLine - c_buffer_pos);
  540.  
  541.         if (EndOfCurrentLine == ConsoleLineBuffer)
  542.         {
  543.         RingWarningBell ();
  544.         return TRUE;
  545.         }
  546.  
  547.         if (--EndOfCurrentLine < c_buffer_pos)
  548.         --c_buffer_pos;
  549.  
  550.            return TRUE;
  551.  
  552.     case KF_DIRECTORY:        /* File name directory        */
  553.         fn_search = TRUE;
  554.  
  555.     case KF_COMPLETE:        /* File name completion        */
  556.     {
  557.         *EndOfCurrentLine = 0;
  558.         return CompleteFileName (c_buffer_pos, fn_search);
  559.     }
  560.  
  561.     case KF_DELETELEFT:        /* Delete left character    */
  562.         if (c_buffer_pos == ConsoleLineBuffer)
  563.         return RingWarningBell ();
  564.  
  565. /* Decrement current position */
  566.  
  567.         --c_buffer_pos;
  568.         memcpy (c_buffer_pos, c_buffer_pos + 1,
  569.             EndOfCurrentLine - c_buffer_pos);
  570.         --EndOfCurrentLine;
  571.         return TRUE;
  572.     }
  573. }
  574.  
  575. /* Set cursor shape */
  576.  
  577. static void near SetCursorShape (bool mode)
  578. {
  579. #ifdef OS2
  580.     VIOCURSORINFO    viociCursor;
  581.  
  582.     VioGetCurType (&viociCursor, 0);
  583.  
  584.     if (mode && KF_List[KF_INSERTCURSOR].akey)
  585.     viociCursor.yStart = (USHORT)((KF_List[KF_HALFHEIGTH].akey
  586.                         ? (viociCursor.cEnd / 2) + 1 : 1));
  587.  
  588.     else
  589.     viociCursor.yStart = (USHORT)(viociCursor.cEnd - 1);
  590.  
  591.     VioSetCurType (&viociCursor, 0);
  592. #else
  593.     union REGS        r;
  594.  
  595. /* Get the current cursor position to get the cursor lines */
  596.  
  597.     r.h.ah = 0x03;
  598.     int86 (0x10, &r, &r);
  599.  
  600. /* Reset the type */
  601.  
  602.     r.h.ah = 0x01;
  603.  
  604.     if (mode && KF_List[KF_INSERTCURSOR].akey)
  605.     r.h.ch = (unsigned char)((KF_List[KF_HALFHEIGTH].akey
  606.                     ? (r.h.cl / 2) + 1 : 1));
  607.  
  608.     else
  609.     r.h.ch = (unsigned char)(r.h.cl - 1);
  610.  
  611.     int86 (0x10, &r, &r);
  612. #endif
  613. }
  614. #endif
  615.  
  616. /* Read Cursor position */
  617.  
  618. static int near ReadCursorPosition (void)
  619. {
  620. #ifdef OS2
  621.     USHORT    usRow;
  622.     USHORT    usColumn;
  623.  
  624.     VioGetCurPos (&usRow, &usColumn, 0);
  625.     return (MaximumColumns * usRow) + usColumn;
  626. #else
  627.     union REGS    r;
  628.  
  629.     fflush (stdout);
  630.     r.h.ah = 0x03;                /* Read cursor position    */
  631.     r.h.bh = 0;                    /* Page zero        */
  632.     int86 (0x10, &r, &r);
  633.     return (r.h.dh * MaximumColumns) + r.h.dl;
  634. #endif
  635. }
  636.  
  637. /* Re-position the cursor */
  638.  
  639. #ifndef NO_HISTORY
  640. static void near SetCursorPosition (int new)
  641. {
  642.     int        diff;
  643. #ifdef OS2
  644.     USHORT    usRow;
  645.     USHORT    usColumn;
  646.  
  647.     fflush (stdout);
  648.  
  649.     usRow = (USHORT)(new / MaximumColumns);
  650.     usColumn = (USHORT)(new % MaximumColumns);
  651.  
  652. /* Are we at the bottom of the page? */
  653.  
  654.     if (usRow >= (unsigned char)MaximumLines)
  655.     {
  656.     diff = usRow + 1 - MaximumLines;
  657.     usRow = (unsigned char)(MaximumLines - 1);
  658.     s_cursor -= MaximumColumns * diff;
  659.     }
  660.  
  661.     VioSetCurPos (usRow, usColumn, 0);
  662. #else
  663.     union REGS    r;
  664.  
  665.     fflush (stdout);
  666.     r.h.ah = 0x02;                /* Set new position    */
  667.     r.h.bh = 0;                    /* Page zero        */
  668.     r.h.dh = (unsigned char)(new / MaximumColumns);
  669.     r.h.dl = (unsigned char)(new % MaximumColumns);
  670.  
  671. /* Are we at the bottom of the page? */
  672.  
  673.     if (r.h.dh >= (unsigned char)MaximumLines)
  674.     {
  675.     diff = r.h.dh + 1 - MaximumLines;
  676.     r.h.dh = (unsigned char)(MaximumLines - 1);
  677.     s_cursor -= MaximumColumns * diff;
  678.     }
  679.  
  680.     int86 (0x10, &r, &r);
  681. #endif
  682. }
  683.  
  684. /* Erase to end of line (avoid need for STUPID ansi.sys memory eater!) */
  685.  
  686. static void near EraseToEndOfLine (void)
  687. {
  688. #ifdef OS2
  689.     BYTE    abCell[2];
  690.     USHORT    usRow;
  691.     USHORT    usColumn;
  692.     USHORT    cb = sizeof (abCell);
  693.  
  694.     fflush (stdout);
  695.  
  696. /* Read attribute under cursor */
  697.  
  698.     VioGetCurPos (&usRow, &usColumn, 0);
  699.     VioReadCellStr (abCell, &cb , usRow, usColumn, 0);
  700.  
  701.     abCell[0] = CHAR_SPACE;
  702.  
  703.     if (m_line < (int)usRow)
  704.     m_line = usRow;
  705.  
  706.     if ((cb = MaximumColumns - usColumn + (m_line - usRow) * MaximumColumns) > 0)
  707.     VioWrtNCell (abCell, cb, usRow, usColumn, 0);
  708. #else
  709.     union REGS        r;
  710.     unsigned char    backg;
  711.  
  712.     fflush (stdout);
  713.  
  714. /* Get the background attribute of the cursor */
  715.  
  716.     r.h.ah = 0x08;
  717.     r.h.bh = 0;
  718.     int86 (0x10, &r, &r);
  719.     backg = (unsigned char)(r.h.ah & 0x07);
  720.  
  721.     r.h.ah = 0x03;
  722.     r.h.bh = 0;
  723.     int86 (0x10, &r, &r);
  724.  
  725. /* Check that we use the correct m_line */
  726.  
  727.     if (m_line < (int)r.h.dh)
  728.     m_line = r.h.dh;
  729.  
  730.     if ((r.x.cx = MaximumColumns - r.h.dl + (m_line - r.h.dh) * MaximumColumns) > 0)
  731.     {
  732.     r.x.ax = 0x0a20;
  733.     r.x.bx = backg;
  734.     int86 (0x10, &r, &r);
  735.     }
  736. #endif
  737. }
  738.  
  739. /* Generate the new cursor position */
  740.  
  741. static void near GenerateNewCursorPosition (void)
  742. {
  743.     char    *cp = ConsoleLineBuffer - 1;
  744.     int        off = s_cursor;
  745.  
  746. /* Search to current position */
  747.  
  748.     while (++cp != c_buffer_pos)
  749.     {
  750.     if (*cp == CHAR_TAB)
  751.         while ((++off) % 8);
  752.  
  753.     else 
  754.         off += (iscntrl (*cp)) ? 2 : 1;
  755.     }
  756.  
  757. /* Position the cursor */
  758.  
  759.     SetCursorPosition (off);
  760. }
  761.  
  762. /* Redisplay the current line */
  763.  
  764. static void near ReDisplayInputLine (void)
  765. {
  766. /* Reposition to start of line */
  767.  
  768.     SetCursorPosition (s_cursor);
  769.  
  770. /* Output the line */
  771.  
  772.     *EndOfCurrentLine = 0;
  773.     DisplayLineWithControl (ConsoleLineBuffer);
  774.  
  775.     if ((m_line = ((s_cursor + Max_Length) / MaximumColumns) + 1) >=
  776.     MaximumLines)
  777.     m_line = MaximumLines - 1;
  778.  
  779.     EraseToEndOfLine ();        /* clear to end of line    */
  780.     Max_Length = EndOfCurrentLine - ConsoleLineBuffer;
  781. }
  782.  
  783. /* Process history command
  784.  *
  785.  * -1: Previous command
  786.  *  1: Next command
  787.  *  0: Current command
  788.  *  2: Current command with no options processing
  789.  */
  790.  
  791. static bool near Process_History (int direction)
  792. {
  793.     char    *optionals = null;
  794.  
  795.     c_buffer_pos = ConsoleLineBuffer;
  796.     EndOfCurrentLine = ConsoleLineBuffer;
  797.     c_history += (direction == 2) ? 0 : direction;
  798.  
  799.     switch (direction)
  800.     {
  801.     case -1:            /* Move up one line        */
  802.         if (c_history < 0)
  803.         {
  804.         c_history = -1;
  805.         return ReStartInput (No_prehistory);
  806.         }
  807.  
  808.         break;
  809.  
  810.     case 1:                /* Move to next history line    */
  811.         if (c_history >= l_history)
  812.         {
  813.         c_history = l_history;
  814.         return ReStartInput (No_posthistory);
  815.         }
  816.  
  817.         break;
  818.  
  819.     case 0:                /* Check out ConsoleLineBuffer    */
  820.         optionals = ConsoleLineBuffer;/* Are there any additions to    */
  821.                     /* the history line        */
  822.  
  823. /* Find the end of the first part */
  824.  
  825.         while (!isspace (*optionals) && *optionals)
  826.         {
  827.         if (*optionals == '!')
  828.         {
  829.  
  830. /* Terminate at !! */
  831.  
  832.             if (*(optionals + 1) == '!')
  833.             {
  834.             optionals += 2;
  835.             break;
  836.             }
  837.  
  838. /* Terminate at a numeric value */
  839.  
  840.             else if (isdigit (*(optionals + 1)) ||
  841.                  (*(optionals + 1) == '-'))
  842.             {
  843.             optionals += 2;
  844.             while (isdigit (*optionals))
  845.                 ++optionals;
  846.  
  847.             break;
  848.             }
  849.         }
  850.  
  851.         ++optionals;
  852.         }
  853.  
  854. /* Copy selected item into line buffer */
  855.  
  856.     case 2:
  857.         M_length = (optionals == null) ? strlen (ConsoleLineBuffer) - 1
  858.                        : optionals - ConsoleLineBuffer - 1;
  859.  
  860.         if (!ScanHistory ())
  861.         return FALSE;
  862.  
  863.         break;
  864.     }
  865.  
  866.     return UpdateConsoleInputLine (optionals);
  867. }
  868.  
  869. /* Ok c_history points to the new line.  Move optionals after history
  870.  * and the copy in history and add a space
  871.  */
  872.  
  873. static bool near UpdateConsoleInputLine (char *optionals)
  874. {
  875.     int        opt_len;
  876.  
  877.     EndOfCurrentLine = &ConsoleLineBuffer[strlen (cmd_history[c_history].command)];
  878.  
  879.     if ((EndOfCurrentLine - ConsoleLineBuffer +
  880.      (opt_len = strlen (optionals)) + 1) >= LINE_MAX)
  881.     return ReStartInput (History_2long);
  882.  
  883.     if (EndOfCurrentLine > optionals)
  884.     memrcpy (EndOfCurrentLine + opt_len, optionals + opt_len, opt_len + 1);
  885.  
  886.     else
  887.     strcpy (EndOfCurrentLine, optionals);
  888.  
  889.     strncpy (ConsoleLineBuffer, cmd_history[c_history].command,
  890.          (EndOfCurrentLine - ConsoleLineBuffer));
  891.     EndOfCurrentLine = &ConsoleLineBuffer[strlen (ConsoleLineBuffer)];
  892.     c_buffer_pos = EndOfCurrentLine;
  893.     return TRUE;
  894. }
  895.  
  896. /* Scan the line buffer for a history match */
  897.  
  898. static bool near ScanHistory (void)
  899. {
  900.     char    *cp = ConsoleLineBuffer + 1;
  901.     char    *ep;
  902.     int        i = (int)strtol (cp, &ep, 10);
  903.  
  904. /* Get the previous command ? (single ! or double !!) */
  905.  
  906.     if ((M_length == 0) || (*cp == '!'))
  907.     {
  908.     if (c_history >= l_history)
  909.         c_history = l_history - 1;
  910.  
  911.     if (c_history < 0)
  912.         return ReStartInput (No_prehistory);
  913.  
  914.     return TRUE;
  915.     }
  916.  
  917. /* Request for special history number item.  Check History file empty */
  918.  
  919.     if (l_history == 0)
  920.     return ReStartInput (No_MatchHistory);
  921.  
  922. /* Check for number */
  923.  
  924.     if ((*ConsoleLineBuffer == '!') && (ep > cp) && M_length)
  925.     {
  926.     M_length = -1;
  927.  
  928.     for (c_history = l_history - 1;
  929.         (c_history >= 0) && (cmd_history[c_history].number != i);
  930.         --c_history)
  931.         continue;
  932.     }
  933.  
  934. /* No - scan for a match */
  935.  
  936.     else
  937.     {
  938.     for (c_history = l_history - 1;
  939.          (c_history >= 0) &&
  940.          (strncmp (cp, cmd_history[c_history].command, M_length) != 0);
  941.          --c_history)
  942.         continue;
  943.     }
  944.  
  945. /* Anything found ? */
  946.  
  947.     if (c_history == -1)
  948.     {
  949.     c_history = l_history - 1;
  950.     return ReStartInput (No_MatchHistory);
  951.     }
  952.  
  953.     return TRUE;
  954. }
  955.  
  956. /* Scan back or forward from current history */
  957.  
  958. static void near PageHistoryRecord (int direction)
  959. {
  960.     c_buffer_pos = ConsoleLineBuffer;
  961.     EndOfCurrentLine = ConsoleLineBuffer;
  962.  
  963.     if (l_history == 0)
  964.     {
  965.     ReStartInput (No_MatchHistory);
  966.     return;
  967.     }
  968.  
  969. /* scan for a match */
  970.  
  971.     while (((c_history += direction) >= 0) && (c_history != l_history) &&
  972.        (strncmp (ConsoleLineBuffer, cmd_history[c_history].command,
  973.              M_length) != 0))
  974.     continue;
  975.  
  976. /* Anything found ? */
  977.  
  978.     if ((c_history < 0) || (c_history >= l_history))
  979.     {
  980.     c_history = l_history - 1;
  981.     ReStartInput (No_MatchHistory);
  982.     }
  983.  
  984.     else
  985.     UpdateConsoleInputLine (null);
  986. }
  987.  
  988. /* Load history file */
  989.  
  990. void LoadHistory (void)
  991. {
  992.     FILE        *fp;
  993.     char        *cp;
  994.     int            i = 0;
  995.     bool        Append = FALSE;
  996.  
  997. /* Initialise history array */
  998.  
  999.     c_history = -1;            /* Current entry        */
  1000.     l_history = 0;            /* End of history array        */
  1001.  
  1002.     if (GetVariableAsString (HistoryFileVariable, FALSE) == null)
  1003.     {
  1004.     SetVariableFromString (HistoryFileVariable,
  1005.                    (cp = BuildFileName ("history.sh")));
  1006.     ReleaseMemoryCell ((void *)cp);
  1007.     }
  1008.  
  1009.     if (GetVariableAsString (HistorySizeVariable, FALSE) == null)
  1010.     SetVariableFromNumeric (HistorySizeVariable, 100L);
  1011.  
  1012.     if ((fp = OpenHistoryFile ("rt")) == (FILE *)NULL)
  1013.     return;
  1014.  
  1015. /* Read in file */
  1016.  
  1017.     cp = ConsoleLineBuffer;
  1018.  
  1019.     while ((i = fgetc (fp)) != EOF)
  1020.     {
  1021.     if (i == 0)
  1022.     {
  1023.         *cp = 0;
  1024.         cp = ConsoleLineBuffer;
  1025.         AddHistory (Append);
  1026.         Append = FALSE;
  1027.     }
  1028.  
  1029.     else if (i == CHAR_NEW_LINE)
  1030.     {
  1031.         *cp = 0;
  1032.         cp = ConsoleLineBuffer;
  1033.         AddHistory (Append);
  1034.         Append = TRUE;
  1035.     }
  1036.  
  1037.     else if ((cp - ConsoleLineBuffer) < LINE_MAX - 2)
  1038.         *(cp++) = (char)i;
  1039.     }
  1040.  
  1041.     fclose (fp);
  1042. }
  1043.  
  1044. /* Open the history file */
  1045.  
  1046. static FILE * near OpenHistoryFile (char *mode)
  1047. {
  1048.     char    *name;
  1049.  
  1050.     if (!HistoryEnabled)
  1051.     return (FILE *)NULL;
  1052.  
  1053.     name = CheckDOSFileName (GetVariableAsString (HistoryFileVariable, FALSE));
  1054.     return fopen (name, mode);
  1055. }
  1056.  
  1057. /* Add entry to history file */
  1058.  
  1059. void AddHistory (bool AppendToLast)
  1060. {
  1061.     char        *cp;
  1062.     struct cmd_history    *cmd;
  1063.     size_t        Len;
  1064.     int            HistorySize;
  1065.  
  1066.     if ((!HistoryEnabled) || (strlen (ConsoleLineBuffer) == 0))
  1067.     return;
  1068.  
  1069. /* Has the size changed ? */
  1070.  
  1071.     HistorySize = (int)GetVariableAsNumeric (HistorySizeVariable);
  1072.  
  1073.     if (HistorySize != CurrentHistorySize)
  1074.     {
  1075.  
  1076. /* Zero - empty history */
  1077.  
  1078.     if (!HistorySize)
  1079.         ClearHistory ();
  1080.  
  1081. /* Allocate a new buffer */
  1082.  
  1083.     else if ((cmd = (struct cmd_history *)GetAllocatedSpace
  1084.                 (sizeof (struct cmd_history) * HistorySize)) ==
  1085.            (struct cmd_history *)NULL)
  1086.         /* DO NOTHING IF NO MEMORY */;
  1087.  
  1088. /* If new buffer is bigger, copy old to new and release */
  1089.  
  1090.     else if ((HistorySize > CurrentHistorySize) ||
  1091.          (l_history < HistorySize))
  1092.     {
  1093.         if (cmd_history != (struct cmd_history *)NULL)
  1094.         {
  1095.         int    Clen;
  1096.  
  1097. /* Calculate the length to copy */
  1098.  
  1099.         Clen = (HistorySize > CurrentHistorySize) 
  1100.             ? CurrentHistorySize : l_history;
  1101.  
  1102.         memcpy (cmd, cmd_history,
  1103.             sizeof (struct cmd_history) * Clen);
  1104.  
  1105. /* Set up new values */
  1106.  
  1107.         ReleaseMemoryCell (cmd_history);
  1108.         }
  1109.  
  1110.         CurrentHistorySize = HistorySize;
  1111.         SetMemoryAreaNumber ((void *)(cmd_history = cmd), 0);
  1112.     }
  1113.  
  1114. /* Less space is available, copy reduced area and update entry numbers */
  1115.  
  1116.     else
  1117.     {
  1118.         int        i = (CurrentHistorySize - HistorySize);
  1119.  
  1120. /* Free entries at bottom */
  1121.  
  1122.         for (Len = 0; (Len < (size_t)i) && (Len < (size_t)l_history); Len++)
  1123.         ReleaseCommandMemory (&cmd_history[Len]);
  1124.  
  1125. /* Transfer entries at top */
  1126.  
  1127.         memcpy (cmd, &cmd_history[i],
  1128.             sizeof (struct cmd_history) * HistorySize);
  1129.  
  1130. /* Update things */
  1131.  
  1132.         if ((c_history -= i) < -1)
  1133.         c_history = -1;
  1134.  
  1135.         if ((l_history -= i) < 0)
  1136.         l_history = 0;
  1137.  
  1138. /* Set up new values */
  1139.  
  1140.         ReleaseMemoryCell (cmd_history);
  1141.         CurrentHistorySize = HistorySize;
  1142.         SetMemoryAreaNumber ((void *)(cmd_history = cmd), 0);
  1143.     }
  1144.     }
  1145.  
  1146. /* If there is no history space - return */
  1147.  
  1148.     if (!CurrentHistorySize)
  1149.     return;
  1150.  
  1151. /* If the array is full, remove the last item */
  1152.  
  1153.     if (l_history == CurrentHistorySize)
  1154.     {
  1155.     ReleaseCommandMemory (&cmd_history[0]);
  1156.  
  1157.     --l_history;
  1158.     memcpy (&cmd_history[0], &cmd_history[1],
  1159.         sizeof (struct cmd_history) * (CurrentHistorySize - 1));
  1160.     }
  1161.  
  1162. /* Save the string.  Is this a PS2 prompt */
  1163.  
  1164.     if ((AppendToLast) && l_history)
  1165.     {
  1166.     cmd = &cmd_history[l_history - 1];
  1167.  
  1168. /* Check length */
  1169.  
  1170.     if ((Len = strlen (cmd->command) + strlen (ConsoleLineBuffer) + 2)
  1171.         >= LINE_MAX)
  1172.     {
  1173.         fprintf (stderr, BasicErrorMessage, LIT_history, History_2long);
  1174.         return;
  1175.     }
  1176.  
  1177. /* Append to buffer, reallocate to new length */
  1178.  
  1179.     if ((cp = GetAllocatedSpace (Len)) == (char *)NULL)
  1180.         return;
  1181.  
  1182.     sprintf (cp, "%s\n%s", cmd->command, ConsoleLineBuffer);
  1183.  
  1184.     ReleaseCommandMemory (cmd);
  1185.     cmd->command = cp;
  1186.     SetMemoryAreaNumber ((void *)cp, 0);
  1187.     }
  1188.  
  1189. /* Save the command line */
  1190.  
  1191.     else
  1192.     {
  1193.     Current_Event = GetLastHistoryEvent ();
  1194.     cmd_history[l_history].number = Current_Event;
  1195.  
  1196.     cmd_history[l_history].command = StringSave (ConsoleLineBuffer);
  1197.     c_history = ++l_history;
  1198.     }
  1199. }
  1200.  
  1201. /* Dump history to file */
  1202.  
  1203. void DumpHistory (void)
  1204. {
  1205.     struct cmd_history    *cp = cmd_history;
  1206.     FILE        *fp = OpenHistoryFile ("wt");
  1207.     int            i;
  1208.  
  1209.     if (fp == (FILE *)NULL)
  1210.     return;
  1211.  
  1212.     for (i = 0; i < l_history; ++cp, ++i)
  1213.     {
  1214.         fputs (cp->command, fp);
  1215.     fputc (0, fp);
  1216.     }
  1217.  
  1218.     fclose (fp);
  1219. }
  1220.  
  1221. /* Clear out history */
  1222.  
  1223. void ClearHistory (void)
  1224. {
  1225.     int            i;
  1226.     struct cmd_history    *cp = cmd_history;
  1227.  
  1228. /* Release the entries */
  1229.  
  1230.     for (i = 0; i < l_history; ++cp, ++i)
  1231.     ReleaseCommandMemory (cp);
  1232.  
  1233.     ReleaseMemoryCell (cmd_history);
  1234.  
  1235. /* Reset data information */
  1236.  
  1237.     c_history = -1;            /* Current entry        */
  1238.     l_history = 0;            /* End of history array        */
  1239.     Current_Event = 0;
  1240.     CurrentHistorySize = 0;
  1241.     cmd_history = (struct cmd_history *)NULL;
  1242. }
  1243.  
  1244. /* Output warning message and prompt */
  1245.  
  1246. static bool near ReStartInput (char *cp)
  1247. {
  1248.     if (cp != (char *)NULL)
  1249.     {
  1250.     if (strlen (ConsoleLineBuffer) && (s_cursor != -1))
  1251.         fputc (CHAR_NEW_LINE, stderr);
  1252.  
  1253.     PrintWarningMessage ("%s: %s", LIT_history, cp);
  1254.     EraseToEndOfLine ();
  1255.     putchar (CHAR_NEW_LINE);
  1256.     }
  1257.  
  1258.     OutputUserPrompt (LastUserPrompt);
  1259.  
  1260. /* Re-initialise */
  1261.  
  1262.     InitialiseInput (InsertMode);
  1263.     return FALSE;
  1264. }
  1265.  
  1266. /* Copy backwards */
  1267.  
  1268. static void near memrcpy (char *sp1, char *sp, int cnt)
  1269. {
  1270.     while (cnt--)
  1271.     *(sp1--) = *(sp--);
  1272. }
  1273.  
  1274. /* Complete file name */
  1275.  
  1276. static bool near CompleteFileName (char *InsertPosition, bool Searching)
  1277. {
  1278.     DIR            *DirectoryHandler;
  1279.     char        d_name [NAME_MAX + 1];
  1280.     struct dirent    *CurrentDE;
  1281.     int            NumberOfMatches = 0;
  1282.     int            max_per_line;
  1283.     static char        *TemporaryDriveString = "a:/";
  1284.     Word_B        *wb = (Word_B *)NULL;
  1285.     char        *SearchString = null;
  1286.     char        *NameStart = InsertPosition;
  1287.     char        *MatchStringStart;
  1288.     char        *StartDirInCLB;
  1289.     char        *DirectoryName;
  1290.     size_t        MatchStringLen;
  1291.     char        *cp;
  1292.     int            i;
  1293.     bool        InsertSpace = TRUE;
  1294.  
  1295. /* Space or at start of line - use NULL file name */
  1296.  
  1297.     if ((NameStart != ConsoleLineBuffer) && isspace (*NameStart) &&
  1298.     !isspace (*(NameStart - 1)))
  1299.     {
  1300.     --NameStart;
  1301.     InsertSpace = FALSE;
  1302.     }
  1303.  
  1304.     if (!isspace (*NameStart))
  1305.     {
  1306.     while (!isspace (*NameStart) && (NameStart != ConsoleLineBuffer))
  1307.         --NameStart;
  1308.  
  1309.     if (isspace (*NameStart))
  1310.         ++NameStart;
  1311.  
  1312.     MatchStringLen = InsertPosition - NameStart + 1;
  1313.  
  1314.     if ((SearchString = AllocateMemoryCell (MatchStringLen)) ==
  1315.                         (char *)NULL)
  1316.         return RingWarningBell ();
  1317.  
  1318.     memcpy (SearchString, NameStart, MatchStringLen - 1);
  1319.     }
  1320.  
  1321. /* Find the directory name */
  1322.  
  1323.     if ((cp = strrchr (SearchString, CHAR_UNIX_DIRECTORY)) != (char *)NULL)
  1324.     {
  1325.     MatchStringStart = cp + 1;
  1326.     DirectoryName = SearchString;
  1327.     StartDirInCLB = NameStart + (int)(MatchStringStart - SearchString);
  1328.     }
  1329.  
  1330. /* No directory flag - Drive specifier? */
  1331.  
  1332.     else if (*(SearchString + 1) == ':')
  1333.     {
  1334.     *(DirectoryName = TemporaryDriveString) = *SearchString;
  1335.     *(DirectoryName + 2) = '.';
  1336.     MatchStringStart = SearchString + 2;
  1337.     StartDirInCLB = NameStart + 2;
  1338.     }
  1339.  
  1340. /* No drive specifier */
  1341.  
  1342.     else
  1343.     {
  1344.     DirectoryName = ".";
  1345.     MatchStringStart = SearchString;
  1346.     StartDirInCLB = NameStart;
  1347.     }
  1348.  
  1349. /* Set up some values - length */
  1350.  
  1351.     MatchStringLen = strlen (MatchStringStart);
  1352.  
  1353. /* Reset the / to a zero to terminate the directory name */
  1354.  
  1355.     if (cp != (char *)NULL)
  1356.     *cp = 0;
  1357.  
  1358. /* Check for some special cases - root */
  1359.  
  1360.     if ((i = strlen (DirectoryName)) == 0)
  1361.     DirectoryName = DirectorySeparator;
  1362.  
  1363.     else if ((i == 2) && (*(DirectoryName + 1) == ':'))
  1364.     {
  1365.     *(DirectoryName = TemporaryDriveString) = *NameStart;
  1366.     *(DirectoryName + 2) = CHAR_UNIX_DIRECTORY;
  1367.     }
  1368.  
  1369. /* Open the directory and search */
  1370.  
  1371.     DirectoryHandler = opendir (CheckDOSFileName (DirectoryName));
  1372.  
  1373.     if (cp != (char *)NULL)
  1374.     *cp = CHAR_UNIX_DIRECTORY;
  1375.  
  1376.     if (DirectoryHandler == (DIR *)NULL)
  1377.     return RingWarningBell ();
  1378.  
  1379. /* Initialise the save buffer for a search or a match.  In the case of a
  1380.  * search, we alway want to output NAME_MAX characters.  In the case of a
  1381.  * match we want to know if we found it.
  1382.  */
  1383.  
  1384.     d_name[NAME_MAX] = 0;
  1385.     *d_name = 0;
  1386.     max_per_line = (MaximumColumns / (((NAME_MAX / 8) + 1) * 8));
  1387.  
  1388. /* Scan the directory */
  1389.  
  1390.     while ((CurrentDE = readdir (DirectoryHandler)) != (struct dirent *)NULL)
  1391.     {
  1392.     if (*CurrentDE->d_name == '.')
  1393.         continue;
  1394.  
  1395.     if (strnicmp (CurrentDE->d_name, MatchStringStart, MatchStringLen) == 0)
  1396.     {
  1397.  
  1398. /* If we are searching, Space fill the name and append it to the word list,
  1399.  * having allocated space for it.
  1400.  */
  1401.  
  1402.         if (Searching)
  1403.         {
  1404.         memset (d_name, CHAR_SPACE, NAME_MAX);
  1405.         memcpy (d_name, CurrentDE->d_name, strlen (CurrentDE->d_name));
  1406.  
  1407.         if ((cp = StringCopy (d_name)) == null)
  1408.         {
  1409.             closedir (DirectoryHandler);
  1410.             return RingWarningBell ();
  1411.         }
  1412.  
  1413.         wb = AddWordToBlock (cp, wb);
  1414.         }
  1415.  
  1416. /* Completing.  If this is the first entry matching, save it */
  1417.  
  1418.         else if (!*d_name && !NumberOfMatches)
  1419.         strcpy (d_name, CurrentDE->d_name);
  1420.  
  1421. /* Second or subsequent, find the longest match */
  1422.  
  1423.         else
  1424.         {
  1425.         for (i = MatchStringLen; d_name[i] == CurrentDE->d_name[i]; i++)
  1426.             continue;
  1427.  
  1428.         d_name[i] = 0;
  1429.         }
  1430.  
  1431. /* Increment counter */
  1432.  
  1433.         ++NumberOfMatches;
  1434.     }
  1435.     }
  1436.  
  1437. /* Finish with the directory */
  1438.  
  1439.     closedir (DirectoryHandler);
  1440.  
  1441. /* If there are no matches, Just ring the bell */
  1442.  
  1443.     if (!NumberOfMatches)
  1444.     return RingWarningBell ();
  1445.  
  1446. /* At this point, we have some data.  If we are searching, sort the
  1447.  * filenames and display them.  Remember to release the memory allocated for
  1448.  * the word block and its entries.
  1449.  */
  1450.  
  1451.     if (Searching)
  1452.     {
  1453.  
  1454. /* Sort the file names and display them */
  1455.  
  1456.     qsort (&wb->w_words[0], wb->w_nword, sizeof (char *), _GP_SortCompare);
  1457.  
  1458. /* Display.  We made all the file names the same length with spaces.  Clever! */
  1459.  
  1460.     for (i = 0; i < wb->w_nword; i++)
  1461.     {
  1462.         putchar ((char)((i % max_per_line == 0) ? CHAR_NEW_LINE
  1463.                              : CHAR_TAB));
  1464.         fputs (wb->w_words[i], stdout);
  1465.         ReleaseMemoryCell ((void *)wb->w_words[i]);
  1466.     }
  1467.  
  1468. /* Release memory */
  1469.  
  1470.     ReleaseMemoryCell ((void *)wb);
  1471.  
  1472. /* Redraw current line */
  1473.  
  1474.     putchar (CHAR_NEW_LINE);
  1475.     OutputUserPrompt (LastUserPrompt);
  1476.     s_cursor = ReadCursorPosition ();
  1477.     return TRUE;
  1478.     }
  1479.  
  1480. /* OK, we are completing.  If the file name is length matches the search
  1481.  * length, there are no unique parts to the filenames in the directory.
  1482.  * Just ring the bell and return.
  1483.  */
  1484.  
  1485.     if ((strlen (d_name) == MatchStringLen) && (NumberOfMatches > 1))
  1486.     return RingWarningBell ();
  1487.  
  1488. /* So, at this point, we are completing and have something to append.  Check
  1489.  * that the line is not too long and if there is an end bit, we can save a
  1490.  * copy of it.
  1491.  */
  1492.  
  1493. /* Insert after spaces */
  1494.  
  1495.     if (InsertSpace && isspace (*InsertPosition) &&
  1496.     (InsertPosition != ConsoleLineBuffer))
  1497.     {
  1498.     ++StartDirInCLB;
  1499.     ++InsertPosition;
  1500.     }
  1501.  
  1502.     cp = null;
  1503.  
  1504.     if (((strlen (InsertPosition) + strlen (d_name) +
  1505.       (StartDirInCLB - ConsoleLineBuffer) + 3) >= LINE_MAX) ||
  1506.     ((i = strlen (InsertPosition)) &&
  1507.      ((cp = StringCopy (InsertPosition)) == null)))
  1508.     return RingWarningBell ();
  1509.  
  1510. /* Append the new end of line bits */
  1511.  
  1512.     strcpy (StartDirInCLB, d_name);
  1513.     strcpy (&StartDirInCLB[i = strlen (StartDirInCLB)], " ");
  1514.  
  1515. /* Save the current position */
  1516.  
  1517.     c_buffer_pos = &ConsoleLineBuffer[strlen (ConsoleLineBuffer)];
  1518.  
  1519. /* If we found only 1 and its a directory, append a d sep */
  1520.  
  1521.     if ((NumberOfMatches == 1) && IsDirectory (NameStart))
  1522.     {
  1523.     StartDirInCLB[i] = CHAR_UNIX_DIRECTORY;
  1524.     strcpy (c_buffer_pos, cp);
  1525.     }
  1526.  
  1527. /* If multiple matches, position at filename and not original position */
  1528.  
  1529.     else
  1530.     {
  1531.     if (isspace (*cp))
  1532.         --c_buffer_pos;
  1533.  
  1534.     strcpy (c_buffer_pos, cp);
  1535.  
  1536.     if ((NumberOfMatches > 1) && !isspace (*cp))
  1537.         --c_buffer_pos;
  1538.     }
  1539.  
  1540. /* Release the saved buffer and reset end of line pointer */
  1541.  
  1542.     if (cp != null)
  1543.     ReleaseMemoryCell ((void *)cp);
  1544.  
  1545.     EndOfCurrentLine = &ConsoleLineBuffer[strlen (ConsoleLineBuffer)];
  1546.  
  1547. /* Beep if more than one */
  1548.  
  1549.     if (NumberOfMatches > 1)
  1550.     RingWarningBell ();
  1551.  
  1552.     return TRUE;
  1553. }
  1554.  
  1555. /* Initialise input */
  1556.  
  1557. static void near InitialiseInput (bool im)
  1558. {
  1559.     c_buffer_pos = ConsoleLineBuffer;    /* Initialise            */
  1560.     EndOfCurrentLine = ConsoleLineBuffer;
  1561.     SetCursorShape (InsertMode = im);
  1562.     M_length = -1;
  1563.  
  1564. /* Reset max line length and get the number of columns */
  1565.  
  1566.     Max_Length = 0;
  1567.     GetScreenParameters ();
  1568.  
  1569. /* Save the cursor position */
  1570.  
  1571.     s_cursor = ReadCursorPosition ();
  1572. }
  1573.  
  1574. /* Configure Keyboard I/O */
  1575.  
  1576. void Configure_Keys (void)
  1577. {
  1578.     char        *cp;            /* Line pointers    */
  1579.     int            i, fval, cval;
  1580.     int            nFields;
  1581.     LineFields        LF;
  1582.     long        value;
  1583.  
  1584. /* Get some memory for the input line and the file name */
  1585.  
  1586.     if ((LF.LineLength = strlen (cp = GetVariableAsString (ShellVariableName,
  1587.                                   FALSE)) + 5) < 200)
  1588.     LF.LineLength = 200;
  1589.  
  1590.     if ((LF.Line = AllocateMemoryCell (LF.LineLength)) == (char *)NULL)
  1591.     return;
  1592.  
  1593.     strcpy (LF.Line, cp);
  1594.  
  1595. /* Find the .exe in the name */
  1596.  
  1597.     if ((cp = strrchr (LF.Line, CHAR_UNIX_DIRECTORY)) != (char *)NULL)
  1598.     ++cp;
  1599.  
  1600.     else
  1601.     cp = LF.Line;
  1602.  
  1603.     if ((cp = strrchr (cp, '.')) == (char *)NULL)
  1604.     cp = &LF.Line[strlen (LF.Line)];
  1605.  
  1606.     strcpy (cp, ".ini");
  1607.  
  1608.     if ((LF.FP = fopen (CheckDOSFileName (LF.Line), "rt")) == (FILE *)NULL)
  1609.     {
  1610.     ReleaseMemoryCell ((void *)LF.Line);
  1611.     return;
  1612.     }
  1613.  
  1614. /* Initialise the internal buffer */
  1615.  
  1616.     LF.Fields = (Word_B *)NULL;
  1617.  
  1618. /* Scan for the file */
  1619.  
  1620.     while ((nFields = ExtractFieldsFromLine (&LF)) != -1)
  1621.     {
  1622.         if (nFields < 2)
  1623.             continue;
  1624.  
  1625. /* Look up the keyword name */
  1626.  
  1627.     for (i = 0;
  1628.          (i < KF_LENGTH) && (stricmp (LF.Fields->w_words[0],
  1629.                  KF_List[i].kf_name) != 0);
  1630.          ++i)
  1631.         continue;
  1632.  
  1633. /* Ignore no matches */
  1634.  
  1635.     if (i == KF_LENGTH)
  1636.         continue;
  1637.  
  1638. /* Get the value of the second field - must be numeric */
  1639.  
  1640.     cval = 0;
  1641.  
  1642.     if (!ConvertNumericValue (LF.Fields->w_words[1], &value, 0))
  1643.         continue;
  1644.  
  1645.     fval = (int)value;
  1646.  
  1647. /* Get the value of the third field, if it exists - must be numeric */
  1648.  
  1649.     if (nFields == 3)
  1650.     {
  1651.         if (!ConvertNumericValue (LF.Fields->w_words[2], &value, 0))
  1652.         continue;
  1653.  
  1654.         cval = (int)value;
  1655.     }
  1656.  
  1657. /* OK we have a valid value, save it */
  1658.  
  1659.     KF_List[i].akey = (char)fval;
  1660.     KF_List[i].fkey = (char)cval;
  1661.     }
  1662.  
  1663.     ReleaseMemoryCell ((void *)LF.Line);
  1664. }
  1665. #endif
  1666.  
  1667. /* Check cursor is in column zero */
  1668.  
  1669. void PositionCursorInColumnZero (void)
  1670. {
  1671. #ifdef OS2
  1672.     BYTE        abCell[2];
  1673.     USHORT        usRow;
  1674.     USHORT        usColumn;
  1675.     USHORT        cb = sizeof (abCell);
  1676. #else
  1677.     union REGS        r;
  1678. #endif
  1679.  
  1680. /* Get screen infor and cursor position */
  1681.  
  1682.     GetScreenParameters ();
  1683.     s_cursor = ReadCursorPosition ();
  1684.  
  1685. #ifdef OS2
  1686.     VioGetCurPos (&usRow, &usColumn, 0);
  1687.     VioReadCellStr (abCell, &cb , usRow, usColumn, 0);
  1688.  
  1689.     if ((s_cursor % MaximumColumns) || (abCell[0] != CHAR_SPACE))
  1690.     putchar (CHAR_NEW_LINE);
  1691. #else
  1692.     r.h.ah = 0x08;
  1693.     r.h.bh = 0x00;
  1694.     int86 (0x10, &r, &r);
  1695.  
  1696.     if ((s_cursor % MaximumColumns) || (r.h.al != CHAR_SPACE))
  1697.     putchar (CHAR_NEW_LINE);
  1698. #endif
  1699. }
  1700.  
  1701. /* Get screen parameters */
  1702.  
  1703. static void near GetScreenParameters (void)
  1704. {
  1705. #ifdef OS2
  1706.     VIOMODEINFO        viomi;
  1707.  
  1708.     viomi.cb = sizeof(viomi);
  1709.  
  1710.     VioGetMode (&viomi, 0);
  1711.     MaximumColumns = viomi.col;
  1712.     MaximumLines = viomi.row;
  1713. #else
  1714.     union REGS        r;
  1715.  
  1716.     MaximumColumns = *(int *)(0x0040004aL);
  1717.     MaximumLines = 25;
  1718.  
  1719. /* Is this an EGA?  This test was found in NANSI.SYS */
  1720.  
  1721.     r.h.ah = 0x12;
  1722.     r.x.bx = 0xff10;
  1723.     int86 (0x10, &r, &r);
  1724.  
  1725. /* Else read the number of rows */
  1726.  
  1727.     if (!(r.x.bx & 0xfefc))
  1728.     {
  1729.     r.x.ax = 0x1130;
  1730.     r.h.bh = 0;
  1731.     int86 (0x10, &r, &r);
  1732.     MaximumLines = r.h.dl + 1;
  1733.     }
  1734. #endif
  1735.  
  1736. /* Set up LINES and COLUMNS variables */
  1737.  
  1738.     SetVariableFromNumeric (LIT_LINES, (long)MaximumLines);
  1739.     SetVariableFromNumeric (LIT_COLUMNS, (long)MaximumColumns);
  1740. }
  1741.  
  1742. /* Ring Bell ? */
  1743.  
  1744. static bool near RingWarningBell (void)
  1745. {
  1746. #ifdef NO_HISTORY
  1747.     putchar (0x07);            /* Ring bell            */
  1748. #else
  1749.     if (KF_List[KF_RINGBELL].akey)
  1750.     putchar (0x07);
  1751. #endif
  1752.  
  1753.     return FALSE;
  1754. }
  1755.  
  1756. /*
  1757.  * Read keyboard function
  1758.  */
  1759.  
  1760. static unsigned char near ReadKeyBoard (unsigned char *f_key)
  1761. {
  1762. #ifdef OS2
  1763.     KBDKEYINFO        kbci;
  1764. #if 0
  1765.     KBDINFO        kbstInfo;
  1766.  
  1767. /* Set keyboard in raw mode.  Can't remember why this has not been removed.
  1768.  * It doesn't do anything
  1769.  */
  1770.  
  1771.     kbstInfo.cb = sizeof (kbstInfo);
  1772.     KbdGetStatus (&kbstInfo, 0);        /* get current status    */
  1773.  
  1774.     kbstInfo.fsMask = (kbstInfo.fsMask &
  1775.                 ~(KEYBOARD_ASCII_MODE | KEYBOARD_ECHO_ON)) |
  1776.                (KEYBOARD_ECHO_OFF | KEYBOARD_BINARY_MODE);
  1777. #endif
  1778.  
  1779. /* Wait for input */
  1780.  
  1781.     KbdCharIn (&kbci, IO_WAIT, 0);
  1782.  
  1783.     if (kbci.chChar == 0x03)
  1784.     raise (SIGINT);
  1785.  
  1786. /* Check for non-function character */
  1787.  
  1788.     if (kbci.chChar && (kbci.chChar != 0xe0))
  1789.     return (unsigned char)kbci.chChar;
  1790.  
  1791. /* Return scan code and type (normal or ALT'ed) */
  1792.  
  1793.     *f_key = (unsigned char)kbci.chScan;
  1794.     return (unsigned char)((kbci.fsState & (ALT | LEFTALT | RIGHTALT))
  1795.                ? 0x0ff : 0);
  1796. #else
  1797.     int        a_key;
  1798.  
  1799. /* If function key or other, get the next keystroke */
  1800.  
  1801.     if (!((a_key = Read_Keyboard ()) & 0x00ff))
  1802.     *f_key = (unsigned char)(Read_Keyboard () & 0x00ff);
  1803.     
  1804.     else
  1805.     *f_key = (unsigned char)(a_key & 0x00ff);
  1806.  
  1807. /* If ALT Key set, return 0xff instead of 0 for function key */
  1808.  
  1809.     if (a_key & 0x0800)
  1810.     a_key = 0x0ff;
  1811.  
  1812.     return (unsigned char)(a_key & 0x00ff);
  1813. #endif
  1814. }
  1815.  
  1816. /*
  1817.  * Update Initialisation value
  1818.  */
  1819.  
  1820. bool    ChangeInitialisationValue (char *string, int newvalue)
  1821. {
  1822.     int        i;
  1823.  
  1824.     for (i = KF_END_FKEYS; i < KF_LENGTH; ++i)
  1825.     {
  1826.     if (stricmp (string, KF_List[i].kf_name) == 0)
  1827.     {
  1828.         KF_List[i].akey = (char)newvalue;
  1829.         return TRUE;
  1830.     }
  1831.     }
  1832.  
  1833.     return FALSE;
  1834. }
  1835.  
  1836. #ifndef OS2
  1837. /*
  1838.  * Enable keyboard polling function - required if DESQview loaded
  1839.  */
  1840.  
  1841. static void near CheckKeyboardPolling (void)
  1842. {
  1843.     union REGS        r;
  1844.  
  1845.     SW_poll = FALSE;        /* No polling required            */
  1846.  
  1847.     r.x.ax = 0x2b01;
  1848.     r.x.cx = 0x4445;
  1849.     r.x.dx = 0x5351;
  1850.     intdos (&r, &r);
  1851.  
  1852.     if (r.h.al != 0xff)
  1853.     SW_poll = TRUE;
  1854. }
  1855. #endif
  1856.  
  1857. #ifndef NO_HISTORY
  1858. /*
  1859.  * Print a single history entry
  1860.  */
  1861.  
  1862. static void near PrintOutHistory (FILE *fp, bool n_flag,
  1863.                   struct cmd_history *cmd)
  1864. {
  1865.     char    *cp = cmd->command;
  1866.  
  1867.     if (n_flag)
  1868.     {
  1869.     fprintf (fp, "%5d: ", cmd->number);
  1870.  
  1871.     while (*cp)
  1872.     {
  1873.         putc (*cp, fp);
  1874.  
  1875.         if (*(cp++) == CHAR_NEW_LINE)
  1876.         fputs ("       ", fp);
  1877.     }
  1878.     }
  1879.  
  1880.     else
  1881.     fputs (cp, fp);
  1882.  
  1883.     putc (CHAR_NEW_LINE, fp);
  1884. }
  1885.  
  1886. /*
  1887.  * Get the last history event number
  1888.  */
  1889.  
  1890. int GetLastHistoryEvent (void)
  1891. {
  1892.     return (l_history) ? cmd_history[l_history - 1].number + 1 : 1;
  1893. }
  1894.  
  1895. /*
  1896.  * Get the last history event
  1897.  */
  1898.  
  1899. char *GetLastHistoryString (void)
  1900. {
  1901.     return l_history ? cmd_history[l_history - 1].command : (char *)NULL;
  1902. }
  1903.  
  1904. /*
  1905.  * Dump history
  1906.  */
  1907.  
  1908. void PrintHistory (bool r_flag, bool n_flag, int First, int Last, FILE *fp)
  1909. {
  1910.     int        i;
  1911.  
  1912.     if (r_flag)
  1913.     {
  1914.     for (i = l_history - 1;
  1915.          (i >= 0) && (cmd_history[i].number >= First); --i)
  1916.     {
  1917.         if (cmd_history[i].number <= Last)
  1918.         PrintOutHistory (fp, n_flag, &cmd_history[i]);
  1919.     }
  1920.     }
  1921.  
  1922.     else
  1923.     {
  1924.     for (i = 0; (i < l_history) && (cmd_history[i].number <= Last); ++i)
  1925.     {
  1926.         if (cmd_history[i].number >= First)
  1927.         PrintOutHistory (fp, n_flag, &cmd_history[i]);
  1928.     }
  1929.     }
  1930. }
  1931.  
  1932. /*
  1933.  * Search for an event
  1934.  */
  1935.  
  1936. int SearchHistory (char *buffer)
  1937. {
  1938.     int        Length = strlen (buffer);
  1939.     int        i;
  1940.  
  1941.     for (i = l_history - 1;
  1942.      (i >= 0) && strncmp (buffer, cmd_history[i].command, Length); --i)
  1943.     continue;
  1944.  
  1945.     return (i == -1) ? -1 : cmd_history[i].number;
  1946. }
  1947.  
  1948. /*
  1949.  * Release Command
  1950.  */
  1951.  
  1952. static void near ReleaseCommandMemory (struct cmd_history *cp)
  1953. {
  1954.     if (cp->command != null)
  1955.     ReleaseMemoryCell ((void *)cp->command);
  1956. }
  1957.  
  1958. /*
  1959.  * Flush history buffer.  If save is set, the contents of the console
  1960.  * buffer will be saved.
  1961.  */
  1962.  
  1963. void    FlushHistoryBuffer (void)
  1964. {
  1965.     if (SaveHistory)
  1966.     AddHistory (AppendHistory);
  1967.  
  1968.     memset (ConsoleLineBuffer, 0, LINE_MAX + 1);
  1969.  
  1970.     AppendHistory = FALSE;
  1971.     SaveHistory = FALSE;
  1972. }
  1973.  
  1974. /*
  1975.  * Save the current console buffer
  1976.  */
  1977.  
  1978. static void near SaveCurrentHistory (void)
  1979. {
  1980.     c_history = l_history;
  1981.  
  1982.     if (SaveHistory)
  1983.     AddHistory (AppendHistory);
  1984.  
  1985.     AppendHistory = (bool)(LastUserPrompt == PS2);
  1986.     SaveHistory = (bool)((LastUserPrompt == PS1) || AppendHistory);
  1987. }
  1988. #endif
  1989.  
  1990. static bool near InsertACharacter (int NewCharacter)
  1991. {
  1992.     if ((EndOfCurrentLine - ConsoleLineBuffer) == LINE_MAX)
  1993.     return RingWarningBell ();    /* Ring bell - line full    */
  1994.  
  1995.     if (c_buffer_pos != EndOfCurrentLine)
  1996.     memrcpy (EndOfCurrentLine + 1, EndOfCurrentLine,
  1997.          EndOfCurrentLine - c_buffer_pos + 1);
  1998.  
  1999.     ++EndOfCurrentLine;
  2000.  
  2001. /* Fast end of line processing */
  2002.  
  2003.     if ((c_buffer_pos == EndOfCurrentLine - 1) && (NewCharacter != CHAR_TAB))
  2004.     {
  2005.     *(c_buffer_pos++) = (char)NewCharacter;
  2006.     OutputACharacter (NewCharacter);
  2007.     return FALSE;
  2008.     }
  2009.  
  2010. /* Not at end of line - redraw */
  2011.  
  2012.     *(c_buffer_pos++) = (char)NewCharacter;
  2013.     return TRUE;
  2014. }
  2015.  
  2016. /*
  2017.  * Delete Last History Item
  2018.  */
  2019.  
  2020. void DeleteLastHistory (void)
  2021. {
  2022.     if (l_history)
  2023.     ReleaseCommandMemory (&cmd_history[--l_history]);
  2024. }
  2025.  
  2026. /*
  2027.  * Clear the screen
  2028.  */
  2029.  
  2030. static bool near ClearScreen (void)
  2031. {
  2032. #ifdef OS2
  2033.     BYTE    abCell[2];
  2034.     USHORT    usRow;
  2035.     USHORT    usColumn;
  2036.     USHORT    cb = sizeof (abCell);
  2037. #else
  2038.     union REGS        r;
  2039.     unsigned char    backg;
  2040. #endif
  2041.  
  2042.     putchar (CHAR_NEW_LINE);
  2043.     fflush (stdout);
  2044.  
  2045. /* Read attribute under cursor */
  2046.  
  2047. #ifdef OS2
  2048.     VioGetCurPos (&usRow, &usColumn, 0);
  2049.     VioReadCellStr (abCell, &cb , usRow, usColumn, 0);
  2050.  
  2051.     abCell[0] = CHAR_SPACE;
  2052.  
  2053.     VioScrollUp (0, 0, 0xffff, 0xffff, 0xffff, abCell, 0);
  2054.     VioSetCurPos (0, 0, 0);
  2055. #else
  2056.  
  2057. /* Get the background attribute of the cursor */
  2058.  
  2059.     r.h.ah = 0x08;
  2060.     r.h.bh = 0;
  2061.     int86 (0x10, &r, &r);
  2062.     backg = (unsigned char)(r.h.ah & 0x07);
  2063.  
  2064. /* Clear the screen */
  2065.  
  2066.     r.x.ax = 0x0600;
  2067.     r.h.bh = backg;
  2068.     r.x.cx = 0;
  2069.     r.h.dh = (unsigned char)MaximumLines;
  2070.     r.h.dl = (unsigned char)MaximumColumns;
  2071.     int86 (0x10, &r, &r);
  2072.  
  2073. /* Position to top of page */
  2074.  
  2075.     r.h.ah = 0x02;                /* Set new position    */
  2076.     r.h.bh = 0;                    /* Page zero        */
  2077.     r.x.dx = 0x0000;
  2078.     int86 (0x10, &r, &r);
  2079. #endif
  2080.  
  2081.     OutputUserPrompt (LastUserPrompt);
  2082.     s_cursor = ReadCursorPosition ();
  2083.     return TRUE;
  2084. }
  2085.  
  2086. /*
  2087.  * Display a line, handling control characters
  2088.  */
  2089.  
  2090. void    DisplayLineWithControl (char *line)
  2091. {
  2092.     int        off = ReadCursorPosition ();
  2093.  
  2094. /* Print characters */
  2095.  
  2096.     while (*line)
  2097.     {
  2098.  
  2099. /* Process TABS */
  2100.  
  2101.     if (*line == CHAR_TAB)
  2102.     {
  2103.         do
  2104.         {
  2105.         putchar (CHAR_SPACE);
  2106.         } while ((++off) % 8);
  2107.     }
  2108.  
  2109. /* Process Control and printing characters */
  2110.  
  2111.     else 
  2112.         off += OutputACharacter (*line);
  2113.  
  2114.     ++line;
  2115.     }
  2116. }
  2117.  
  2118. /*
  2119.  * Get the Root Disk Drive.  If not defined, set it to the current drive.
  2120.  */
  2121.  
  2122. int    GetRootDiskDrive (void)
  2123. {
  2124.     if (!KF_List[KF_ROOTDRIVE].akey)
  2125.     KF_List[KF_ROOTDRIVE].akey = (char)GetCurrentDrive ();
  2126.  
  2127.     return KF_List[KF_ROOTDRIVE].akey;
  2128. }
  2129.  
  2130. /*
  2131.  * Get the EOF Key
  2132.  */
  2133.  
  2134. int    GetEOFKey (void)
  2135. {
  2136.     if (!KF_List[KF_EOFKEY].akey)
  2137.     KF_List[KF_EOFKEY].akey = 0x1a;
  2138.  
  2139.     return KF_List[KF_EOFKEY].akey;
  2140. }
  2141.  
  2142. /*
  2143.  * Output a Character - excluding TABS.  Return # chars output.
  2144.  *
  2145.  * TABS are not checked for
  2146.  */
  2147.  
  2148. static int near OutputACharacter (register int c)
  2149. {
  2150.     register int    off = 1;
  2151.  
  2152. /* Check for control and process */
  2153.  
  2154.     if (iscntrl (c))
  2155.     {
  2156.     putchar (CHAR_NOT);
  2157.     c += '@';
  2158.     off++;
  2159.     }
  2160.  
  2161. /* Output the character */
  2162.  
  2163.     putchar (c);
  2164.     return off;
  2165. }
  2166.